/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_SLOT_H
#define M_SLOT_H

#include <stdexcept>

#include "slothelper.h"
#include "refptr.h"


namespace Engine
{
	/**
	*   Policy class - defines that signal will be passed to slot immediately.
	*/
	class ImmediateExecute
	{	
	};

	/**
	*   Policy class - defines that signal will be passed to slot after message queue will be processed.
	*/
	class LaterExecute
	{
	};

	/** 
	*   Base class for slots.
	*   Provide virtual functions for executing.
	*/
	template<class Arg0=SlotHelper::unusable,
			 class Arg1=SlotHelper::unusable,
			 class Arg2=SlotHelper::unusable,
			 class Arg3=SlotHelper::unusable,
			 class Arg4=SlotHelper::unusable,
			 class Arg5=SlotHelper::unusable,
			 class Arg6=SlotHelper::unusable,
			 class Arg7=SlotHelper::unusable
			>
	class SlotBase
	{
	public:
		virtual void Execute()=0;
		virtual void Execute(Arg0 arg0)=0;
		virtual void Execute(Arg0 arg0,Arg1 arg1)=0;
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2)=0;
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3)=0;
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4)=0;
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5)=0;
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5,Arg6 arg6)=0;
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5,Arg6 arg6,Arg7 arg7)=0;
		virtual const void* GetObj()=0;
		virtual ~SlotBase(){}			
	};

	/** 
	*   Slot implementation.
	*/
	template<class ExecutePolicy,
		     class T,			 
			 class Arg0=SlotHelper::unusable,
			 class Arg1=SlotHelper::unusable,
			 class Arg2=SlotHelper::unusable,
			 class Arg3=SlotHelper::unusable,
			 class Arg4=SlotHelper::unusable,
			 class Arg5=SlotHelper::unusable,
			 class Arg6=SlotHelper::unusable,
			 class Arg7=SlotHelper::unusable
			>
	class Slot:public SlotBase<Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7>
	{
	public:
		typedef void (T::*Function0)(); ///< type of callback function pointer.
		typedef void (T::*Function1)(Arg0); ///< type of callback function pointer.
		typedef void (T::*Function2)(Arg0, Arg1); ///< type of callback function pointer.
		typedef void (T::*Function3)(Arg0, Arg1, Arg2); ///< type of callback function pointer.
		typedef void (T::*Function4)(Arg0, Arg1, Arg2, Arg3); ///< type of callback function pointer.
		typedef void (T::*Function5)(Arg0, Arg1, Arg2, Arg3, Arg4); ///< type of callback function pointer.
		typedef void (T::*Function6)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5); ///< type of callback function pointer.
		typedef void (T::*Function7)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); ///< type of callback function pointer.
		typedef void (T::*Function8)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7); ///< type of callback function pointer.

		/** 
		*   Constructor.
		*   Also calculate number of specified template parameters.
		*/
		Slot():m_func0(NULL),m_obj(NULL)
		{
			m_funcIndex = SlotHelper::is_usable<Arg0>::value+SlotHelper::is_usable<Arg1>::value+SlotHelper::is_usable<Arg2>::value+SlotHelper::is_usable<Arg3>::value+SlotHelper::is_usable<Arg4>::value+SlotHelper::is_usable<Arg5>::value+SlotHelper::is_usable<Arg6>::value+SlotHelper::is_usable<Arg7>::value;
		}

		/** 
		*   Copy constructor.
		*   Also calculate number of specified template parameters.
		*/
		Slot(const Slot& sl)
		{
			m_funcIndex = sl.m_funcIndex;
			m_obj = sl.m_obj;
			m_func0 = sl.m_func0;
		}
		
		/** 
		*   Initialize slot with object and pointer to function.
		*   @param obj pointer to object.
		*   @param func pointer to objects member.
		*/
		void Init(T* obj,Function0 func)
		{
			m_obj=obj;
			m_func0=func;
			
		}
		void Init(T* obj,Function1 func)
		{
			m_obj=obj;
			m_func1=func;
			
		}
		void Init(T* obj,Function2 func)
		{
			m_obj=obj;
			m_func2=func;
			
		}
		void Init(T* obj,Function3 func)
		{
			m_obj=obj;
			m_func3=func;
			
		}
		void Init(T* obj,Function4 func)
		{
			m_obj=obj;
			m_func4=func;
			
		}
		void Init(T* obj,Function5 func)
		{
			m_obj=obj;
			m_func5=func;
			
		}
		void Init(T* obj,Function6 func)
		{
			m_obj=obj;
			m_func6=func;
			
		}
		void Init(T* obj,Function7 func)
		{
			m_obj=obj;
			m_func7=func;
			
		}
		void Init(T* obj,Function8 func)
		{
			m_obj=obj;
			m_func8=func;
			
		}

		/** 
		*   Execute callback with some parameters.
		*/		
		virtual void Execute()
		{
			if(m_obj!=NULL&&m_func0!=NULL&&m_funcIndex==0)
			{
				SlotMessage0<Slot<ImmediateExecute,T> >* msg = new SlotMessage0<Slot<ImmediateExecute,T> >();
				msg->Init(m_obj, m_func0);
				m_obj->AddMessage(RefPtr<SlotMessageBase>(msg));
			}
			else
				throw std::invalid_argument("Wrong parameters");
		}

		virtual void Execute(Arg0 arg0)
		{
			if(m_obj!=NULL&&m_func1!=NULL&&m_funcIndex==1)
			{
				SlotMessage1<Slot<ImmediateExecute,T,Arg0>,Arg0>* msg = new SlotMessage1<Slot<ImmediateExecute,T,Arg0>,Arg0>(arg0);
				msg->Init(m_obj, m_func1);
				m_obj->AddMessage(RefPtr<SlotMessageBase>(msg));
			}
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1)
		{
			if(m_obj!=NULL&&m_func2!=NULL&&m_funcIndex==2)
			{
				SlotMessage2<Slot<ImmediateExecute,T,Arg0,Arg1>,Arg0,Arg1>* msg = new SlotMessage2<Slot<ImmediateExecute,T,Arg0,Arg1>,Arg0,Arg1>(arg0,arg1);
				msg->Init(m_obj, m_func2);
				m_obj->AddMessage(RefPtr<SlotMessageBase>(msg));
			}
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2)
		{
			if(m_obj!=NULL&&m_func3!=NULL&&m_funcIndex==3)
			{
				SlotMessage3<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2>,Arg0,Arg1,Arg2>* msg = new SlotMessage3<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2>,Arg0,Arg1,Arg2>(arg0,arg1,arg2);
				msg->Init(m_obj, m_func3);
				m_obj->AddMessage(RefPtr<SlotMessageBase>(msg));
			}
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3)
		{
			if(m_obj!=NULL&&m_func4!=NULL&&m_funcIndex==4)
			{
				SlotMessage4<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3>,Arg0,Arg1,Arg2,Arg3>* msg = new SlotMessage4<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3>,Arg0,Arg1,Arg2,Arg3>(arg0,arg1,arg2,arg3);
				msg->Init(m_obj, m_func4);
				m_obj->AddMessage(RefPtr<SlotMessageBase>(msg));
			}
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4)
		{
			if(m_obj!=NULL&&m_func5!=NULL&&m_funcIndex==5)
			{
				SlotMessage5<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3,Arg4>,Arg0,Arg1,Arg2,Arg3,Arg4>* msg = new SlotMessage5<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3,Arg4>,Arg0,Arg1,Arg2,Arg3,Arg4>(arg0,arg1,arg2,arg3,arg4);
				msg->Init(m_obj, m_func5);
				m_obj->AddMessage(RefPtr<SlotMessageBase>(msg));
			}
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5)
		{
			if(m_obj!=NULL&&m_func6!=NULL&&m_funcIndex==6)
			{
				SlotMessage6<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5>,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5>* msg = new SlotMessage6<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5>,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5>(arg0,arg1,arg2,arg3,arg4,arg5);
				msg->Init(m_obj, m_func6);
				m_obj->AddMessage(RefPtr<SlotMessageBase>(msg));
			}
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5,Arg6 arg6)
		{
			if(m_obj!=NULL&&m_func7!=NULL&&m_funcIndex==7)
			{
				SlotMessage7<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6>,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6>* msg = new SlotMessage7<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6>,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6>(arg0,arg1,arg2,arg3,arg4,arg5,arg6);
				msg->Init(m_obj, m_func7);
				m_obj->AddMessage(RefPtr<SlotMessageBase>(msg));
			}
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5,Arg6 arg6,Arg7 arg7)
		{
			if(m_obj!=NULL&&m_func8!=NULL&&m_funcIndex==8)
			{
				SlotMessage8<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7>,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7>* msg = new SlotMessage8<Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7>,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7>(arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7);
				msg->Init(m_obj, m_func8);
				m_obj->AddMessage(RefPtr<SlotMessageBase>(msg));
			}
			else
				throw std::invalid_argument("Wrong parameters");
		}
		
		/** 
		*   Return pointer to callback object(used in Signal for Disconnect).
		*/
		virtual const void* GetObj()
		{
			return m_obj;
		}
						
	private:

		/** 
		*   Pointer to callback function.
		*/
		union
		{
			Function0 m_func0;
			Function1 m_func1;
			Function2 m_func2;
			Function3 m_func3;
			Function4 m_func4;
			Function5 m_func5;
			Function6 m_func6;
			Function7 m_func7;
			Function8 m_func8;
		};

		T*   m_obj;         ///< pointer to callback object.

		char m_funcIndex;   ///< index of callback function from union(used in Execute).		
	};


	/** 
	*   Slot implementation.
	*/
	template<
			 class T,			 
			 class Arg0,
			 class Arg1,
			 class Arg2,
			 class Arg3,
			 class Arg4,
			 class Arg5,
			 class Arg6,
			 class Arg7
			>
	class Slot<ImmediateExecute,T,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7>:public SlotBase<Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7>
	{
	public:
		typedef void (T::*Function0)(); ///< type of callback function pointer.
		typedef void (T::*Function1)(Arg0); ///< type of callback function pointer.
		typedef void (T::*Function2)(Arg0, Arg1); ///< type of callback function pointer.
		typedef void (T::*Function3)(Arg0, Arg1, Arg2); ///< type of callback function pointer.
		typedef void (T::*Function4)(Arg0, Arg1, Arg2, Arg3); ///< type of callback function pointer.
		typedef void (T::*Function5)(Arg0, Arg1, Arg2, Arg3, Arg4); ///< type of callback function pointer.
		typedef void (T::*Function6)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5); ///< type of callback function pointer.
		typedef void (T::*Function7)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); ///< type of callback function pointer.
		typedef void (T::*Function8)(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7); ///< type of callback function pointer.

		/** 
		*   Constructor.
		*   Also calculate number of specified template parameters.
		*/
		Slot():m_func0(NULL),m_obj(NULL)
		{
			m_funcIndex = SlotHelper::is_usable<Arg0>::value+SlotHelper::is_usable<Arg1>::value+SlotHelper::is_usable<Arg2>::value+SlotHelper::is_usable<Arg3>::value+SlotHelper::is_usable<Arg4>::value+SlotHelper::is_usable<Arg5>::value+SlotHelper::is_usable<Arg6>::value+SlotHelper::is_usable<Arg7>::value;
		}
		
		/** 
		*   Copy constructor.
		*   Also calculate number of specified template parameters.
		*/
		Slot(const Slot& sl)
		{
			m_funcIndex = sl.m_funcIndex;
			m_obj = sl.m_obj;
			m_func0 = sl.m_func0;
		}

		/** 
		*   Initialize slot with object and pointer to function.
		*   @param obj pointer to object.
		*   @param func pointer to objects member.
		*/
		void Init(T* obj,Function0 func)
		{
			m_obj=obj;
			m_func0=func;
			
		}
		void Init(T* obj,Function1 func)
		{
			m_obj=obj;
			m_func1=func;
			
		}
		void Init(T* obj,Function2 func)
		{
			m_obj=obj;
			m_func2=func;
			
		}
		void Init(T* obj,Function3 func)
		{
			m_obj=obj;
			m_func3=func;
			
		}
		void Init(T* obj,Function4 func)
		{
			m_obj=obj;
			m_func4=func;
			
		}
		void Init(T* obj,Function5 func)
		{
			m_obj=obj;
			m_func5=func;
			
		}
		void Init(T* obj,Function6 func)
		{
			m_obj=obj;
			m_func6=func;
			
		}
		void Init(T* obj,Function7 func)
		{
			m_obj=obj;
			m_func7=func;
			
		}
		void Init(T* obj,Function8 func)
		{
			m_obj=obj;
			m_func8=func;
			
		}

		/** 
		*   Execute callback with some parameters.
		*/		
		virtual void Execute()
		{
			if(m_obj!=NULL&&m_func0!=NULL&&m_funcIndex==0)
				(m_obj->*m_func0)();
			else
				throw std::invalid_argument("Wrong parameters");
		}
		
		virtual void Execute(Arg0 arg0)
		{
			if(m_obj!=NULL&&m_func1!=NULL&&m_funcIndex==1)				
				(m_obj->*m_func1)(arg0);
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1)
		{
			if(m_obj!=NULL&&m_func2!=NULL&&m_funcIndex==2)
				(m_obj->*m_func2)(arg0,arg1);
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2)
		{
			if(m_obj!=NULL&&m_func3!=NULL&&m_funcIndex==3)
				(m_obj->*m_func3)(arg0,arg1,arg2);
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3)
		{
			if(m_obj!=NULL&&m_func4!=NULL&&m_funcIndex==4)
				(m_obj->*m_func4)(arg0,arg1,arg2,arg3);
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4)
		{
			if(m_obj!=NULL&&m_func5!=NULL&&m_funcIndex==5)
					(m_obj->*m_func5)(arg0,arg1,arg2,arg3,arg4);
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5)
		{
			if(m_obj!=NULL&&m_func6!=NULL&&m_funcIndex==6)
				(m_obj->*m_func6)(arg0,arg1,arg2,arg3,arg4,arg5);
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5,Arg6 arg6)
		{
			if(m_obj!=NULL&&m_func7!=NULL&&m_funcIndex==7)
				(m_obj->*m_func7)(arg0,arg1,arg2,arg3,arg4,arg5,arg6);
			else
				throw std::invalid_argument("Wrong parameters");
		}
		virtual void Execute(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5,Arg6 arg6,Arg7 arg7)
		{
			if(m_obj!=NULL&&m_func8!=NULL&&m_funcIndex==8)
				(m_obj->*m_func8)(arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7);
			else
				throw std::invalid_argument("Wrong parameters");
		}
		
		/** 
		*   Return pointer to callback object(used in Signal for Disconnect).
		*/
		virtual const void* GetObj()
		{
			return m_obj;
		}					

	private:

		/** 
		*   Pointer to callback function.
		*/
		union
		{
			Function0 m_func0;
			Function1 m_func1;
			Function2 m_func2;
			Function3 m_func3;
			Function4 m_func4;
			Function5 m_func5;
			Function6 m_func6;
			Function7 m_func7;
			Function8 m_func8;
		};

		T*   m_obj;         ///< pointer to callback object.

		char m_funcIndex;   ///< index of callback function from union(used in Execute).		
	};

}

#endif
